package com.xdap.materialselect.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.definesys.mpaas.common.http.Response;

import com.xdap.api.moudle.roles.pojo.entity.ApaasRoleUserParams;
import com.xdap.api.moudle.watercode.pojo.XdapWaterCodeStream;
import com.xdap.materialselect.dao.MaterialSelectDao;
import com.xdap.materialselect.entity.dto.*;
import com.xdap.materialselect.entity.po.*;
import com.xdap.materialselect.entity.vo.ReportDataVo;
import com.xdap.materialselect.service.MaterialSelectService;
import com.xdap.materialselect.util.MaSelectCommonUtil;


import com.xdap.runtime.service.RuntimeAppContextService;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.aggregation.ArrayOperators;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Auther:huhanrui
 * @Date:2025/7/16 13:15
 */
@Service
@Slf4j
public class MaterialSelectServiceImpl implements MaterialSelectService {

    @Autowired
    private MaterialSelectDao materialSelectDao;

    @Autowired
    private MaSelectCommonUtil maSelectCommonUtil;

    @Value("${handle.waterRuleId}")
    private String waterRuleId;


    @Value("${handle.componentId}")
    private String componentId;

    @Autowired
    private RuntimeAppContextService runtimeAppContextService;

    @Override
    public List<HeaderDto> getReportHeader(ReportQueryDto reportQueryDto) {
        //基地权限过滤
        reportQueryDto = addBasePower(reportQueryDto);
        List<HeaderDto> headers = new ArrayList<>();

        headers.add(HeaderDto.builder().key("batch").label("批号").build());
        headers.add(HeaderDto.builder().key("materialModel").label("规格型号").build());
        headers.add(HeaderDto.builder().key("balanceWeight").label("结存").build());
        headers.add(HeaderDto.builder().key("storageDate").label("入库日期").build());
        headers.add(HeaderDto.builder().key("storageAge").label("库龄").build());
        headers.add(HeaderDto.builder().key("tagName").label("分类标签").build());
        headers.add(HeaderDto.builder().key("currentWeightKg").label("可用库存").build());
        headers.add(HeaderDto.builder().key("remark").label("挑料备注").build());
        headers.add(HeaderDto.builder().key("customerCode").label("检测自编号").build());
        headers.add(HeaderDto.builder().key("judgeResult").label("判定结果").build());

        List<HeaderDto> dynamicHeaders = new ArrayList<>();
        List<TestItemHeaderDto> testItemHeaderDtoList = new ArrayList<>();
        List<TestItemHeaderDto> selectItemHeaderDtoList = new ArrayList<>();
        //有挑料单的表头
        String materialSelectId = reportQueryDto.getMaterialSelectId();
        if (StringUtils.isNotBlank(materialSelectId)) {
            //查询：【控制标准详情】
            TmaterialSelectionCustomer tmaterialSelectionCustomer = materialSelectDao.getMaterialSelectById(materialSelectId);
            selectItemHeaderDtoList = materialSelectDao.getProjectByTabId(tmaterialSelectionCustomer.getDocumentId());
        }
        //没有挑料单的表头
        List<TestItemHeaderDto>  allItemHeaderDtoList = materialSelectDao.getSelectReportHeader(reportQueryDto);
        if(CollectionUtils.isNotEmpty(selectItemHeaderDtoList)){
            List<String> selectItemProjectIds = selectItemHeaderDtoList.stream().map(m -> m.getProjectId()).collect(Collectors.toList());
            allItemHeaderDtoList = allItemHeaderDtoList.stream().filter(f -> !selectItemProjectIds.contains(f.getProjectId())).collect(Collectors.toList());
            allItemHeaderDtoList.addAll(selectItemHeaderDtoList);
        }
        testItemHeaderDtoList.addAll(allItemHeaderDtoList);

        testItemHeaderDtoList = testItemHeaderDtoList.stream().filter(f -> StringUtils.isNotBlank(f.getProjectName())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(testItemHeaderDtoList)) {
            Map<String, List<TestItemHeaderDto>> typeGroup = testItemHeaderDtoList.stream().collect(Collectors.groupingBy(TestItemHeaderDto::getProjectType));
            for (String type : typeGroup.keySet()) {
                List<TestItemHeaderDto> typeItems = typeGroup.get(type);
                // 对 typeItems 按照 TSeq 字段进行升序排序
                typeItems.sort(Comparator.comparing(TestItemHeaderDto::getSortOrder, Comparator.nullsLast(Integer::compareTo)));
                Optional<TestItemHeaderDto> minTSeqItem = typeItems.stream()
                        .min(Comparator.comparing(TestItemHeaderDto::getSortOrder, Comparator.nullsLast(Comparator.naturalOrder())));
                // 如果需要获取对象（如果存在）
                TestItemHeaderDto typeInfo = minTSeqItem.orElse(null); // 或者使用其他默认值
                HeaderDto typeHeader = HeaderDto.builder().key("Type-" + type).label(type).sortOrder(typeInfo.getSortOrder()).build();

                List<HeaderProjectDto> childrens = new ArrayList<>();
                for (TestItemHeaderDto typeItem : typeItems) {
                    HeaderProjectDto childItem = HeaderProjectDto.builder().key("Project-" + typeItem.getProjectName()).label(typeItem.getProjectName()).sortOrder(typeItem.getSortOrder()).build();
                    Double tLsl = typeItem.getTLsl();
                    Double tUsl = typeItem.getTUsl();
                    if (tLsl != null && tUsl != null) {
                        childItem.setScope(tLsl + "-" + tUsl);
                    } else if (tLsl != null) {
                        childItem.setScope(">=" + tLsl);
                    } else if (tUsl != null) {
                        childItem.setScope("<=" + tUsl);
                    }
                    childItem.setTLsl(tLsl);
                    childItem.setTUsl(tUsl);
                    childrens.add(childItem);
                }
                typeHeader.setChildren(childrens);
                dynamicHeaders.add(typeHeader);
            }
        }
        dynamicHeaders.sort(Comparator.comparing(HeaderDto::getSortOrder));
        headers.addAll(dynamicHeaders);
        return headers;
    }

    @Override
    public Response getReportData(ReportQueryDto reportQueryDto) {
        //基地权限过滤
        reportQueryDto = addBasePower(reportQueryDto);
        List<Map<String, Object>> dataMapList = new ArrayList<>();
        List<ReportDataVo> reportDataVoList = materialSelectDao.getSelectReportDate(reportQueryDto);
        //获取表头
        List<HeaderDto> reportHeader = this.getReportHeader(reportQueryDto);
        List<HeaderProjectDto> headerProjectDtos = flattenChildren(reportHeader);
        Map<String, HeaderProjectDto> headerProjectMap = headerProjectDtos.stream().collect(Collectors.toMap(m -> m.getKey(), m -> m, (k1, k2) -> k1));
        //处理报表数据
        handleReportData(reportDataVoList, dataMapList, headerProjectMap, reportQueryDto);

        Set<Map<String, Object>> combinedList = new HashSet<>();
        Boolean isGroupQuery = false;

        //和条件组
        if (CollectionUtils.isNotEmpty(reportQueryDto.getAndProjectList())) {
            isGroupQuery = true;
            List<String> projectIdList = reportQueryDto.getAndProjectList().stream().map(m -> m.getProjectId()).collect(Collectors.toList());
            //根据id取项目名称
            List<TtestItem> projects = materialSelectDao.getProjectByIds(projectIdList);
            Map<String, String> projectMap = projects.stream().collect(Collectors.toMap(m -> m.getId(), m -> m.getTestItemName()));
            //一条数据同时满足多个条件
            for (Map<String, Object> dataMap : dataMapList) {
                Integer passNum = 0;
                for (ProjectQueryDto projectQueryDto : reportQueryDto.getAndProjectList()) {
                    String projectId = projectQueryDto.getProjectId();
                    Double minValue = projectQueryDto.getMinValue();
                    Double maxValue = projectQueryDto.getMaxValue();
                    DynamicDataCell dynamicDataCell = (DynamicDataCell) dataMap.get("Project-" + projectMap.get(projectId));
                    if (dynamicDataCell != null) {
                        String dataValue = dynamicDataCell.getValue();
                        boolean inRange = isInRange(dataValue, minValue, maxValue);
                        if (inRange) {
                            passNum = passNum + 1;
                        }
                    }
                }
                if (passNum == reportQueryDto.getAndProjectList().size()) {
                    combinedList.add(dataMap);
                }
            }
        }
        //或条件组
        if (CollectionUtils.isNotEmpty(reportQueryDto.getOrProjectList())) {
            isGroupQuery = true;
            List<String> projectIdList = reportQueryDto.getOrProjectList().stream().map(m -> m.getProjectId()).collect(Collectors.toList());
            List<TtestItem> projects = materialSelectDao.getProjectByIds(projectIdList);
            Map<String, String> projectMap = projects.stream().collect(Collectors.toMap(m -> m.getId(), m -> m.getTestItemName()));
            //一条数据满足任意一个条件
            for (ProjectQueryDto projectQueryDto : reportQueryDto.getAndProjectList()) {
                String projectId = projectQueryDto.getProjectId();
                Double minValue = projectQueryDto.getMinValue();
                Double maxValue = projectQueryDto.getMaxValue();
                for (Map<String, Object> dataMap : dataMapList) {
                    DynamicDataCell dynamicDataCell = (DynamicDataCell) dataMap.get("Project-" + projectMap.get(projectId));
                    if (dynamicDataCell != null) {
                        String dataValue = dynamicDataCell.getValue();
                        boolean inRange = isInRange(dataValue, minValue, maxValue);
                        if (inRange) {
                            combinedList.add(dataMap);
                        }
                    }
                }
            }
        }
        if (CollectionUtils.isNotEmpty(combinedList) || isGroupQuery) {
            //取两个集合的交集
            dataMapList.retainAll(combinedList);
        }


        //排序
        dataMapList = sortDataMapList(dataMapList);

        // 分页处理
        int page = reportQueryDto.getPage() != null ? reportQueryDto.getPage() : 1;
        int pageSize = reportQueryDto.getPageSize() != null ? reportQueryDto.getPageSize() : dataMapList.size();
        // 计算分页起始和结束索引
        int total = dataMapList.size();
        int startIndex = (page - 1) * pageSize;
        int endIndex = Math.min(startIndex + pageSize, total);

        // 确保索引有效
        if (startIndex < total && startIndex >= 0) {
            List<Map<String, Object>> pagedData = dataMapList.subList(startIndex, endIndex);
            return Response.ok().table(pagedData).setTotal(total);
        } else {
            // 如果分页参数超出范围，返回空列表
            return Response.ok().table(new ArrayList<>()).setTotal(total);
        }
    }

    @Override
    public Response addTypeTag(ReportQueryDto reportQueryDto) {
        try {
            List<String> batchNoList = reportQueryDto.getBatchNoList();
            String staticTagId = reportQueryDto.getStaticTag();
            String staticTag = MaSelectCommonUtil.insertBracket(staticTagId);
            //在原来的基础上，增加逻辑，如果状态是锁定，并且有标签，不更新
            //更新包次=当 在库状态 不是 已出库 批号再batchNoList中，更新分类标签为 staticTag
            List<TmatInfo> packList = materialSelectDao.getPackByNos(batchNoList);
            packList = packList.stream()
                    .filter(f -> !"[\"OUT\"]".equals(f.getPickStatus()))
                    .filter(f -> !(f.getStaticTag() != null && "[\"LOCK\"]".equals(f.getPickStatus()))).collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(packList)){
                List<String> packListIds = packList.stream().map(m -> m.getId()).collect(Collectors.toList());
                materialSelectDao.updatePackStaticTag(packListIds, staticTag);
            }

            //更新批次= 批号在batchNoList中，更新分类标签为 staticTag
            List<TbatchCommonInfo> batchList = materialSelectDao.getBatchByNos(batchNoList);
            batchList = batchList.stream().filter(f -> !(f.getStaticTag() != null && "[\"LOCK\"]".equals(f.getPickStatus()))).collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(batchList)){
                List<String> batchListIds = batchList.stream().map(m -> m.getId()).collect(Collectors.toList());
                materialSelectDao.updatebatchStaticTag(batchListIds, staticTag);
            }
            return Response.ok();
        } catch (Exception e) {
            e.printStackTrace();
            return Response.error("添加标签失败");
        }
    }

    @Override
    public Response genHandleData(ReportQueryDto reportQueryDto) {
        log.info("批量生成处置单入参===>{}", JSONObject.toJSONString(reportQueryDto));
        List<String> batchNoList = reportQueryDto.getBatchNoList();
        String handleRemark = reportQueryDto.getHandleRemark();
        String handleType = reportQueryDto.getHandleType();
        String handleWay = reportQueryDto.getHandleWay();
        String handleTag = reportQueryDto.getHandleTag();
        String timestamp = String.valueOf(System.currentTimeMillis());

        //查询可处置包次信息 查询包次-批次号在batchNoList中，库存状态是在库
        List<TmatInfo> packForHandle = materialSelectDao.getPackForHandle(batchNoList);
        //按找批次进行分组，计算每组的结存重量的总和
        Map<String, List<TmatInfo>> packForHandleMap = packForHandle.stream().collect(Collectors.groupingBy(TmatInfo::getBatchNo));
        //查询批次
        List<TbatchCommonInfo> batchForHandle = materialSelectDao.getBatchForHandle(batchNoList);
        Map<String, TbatchCommonInfo> batchForHandleMap = batchForHandle.stream().collect(Collectors.toMap(m -> m.getBatchNo(), m -> m, (m1, m2) -> m1));
        List<GcsBatchHanle> gcsBatchHanleList = new ArrayList<>();
        List<TbatchCommonInfo> updateBatchList = new ArrayList<>();
        List<TmatInfo> updatePackList = new ArrayList<>();
        // 定义格式化器
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
        // 格式化日期
        String dateStr = LocalDate.now().format(formatter);
        for (String batch : packForHandleMap.keySet()) {
            List<TmatInfo> packItemList = packForHandleMap.get(batch);
            TbatchCommonInfo batchItem = batchForHandleMap.get(batch);
            //批次锁定重量
            Double batchLockWeight = packItemList.stream().map(TmatInfo::getBalanceWeightKg).filter(Objects::nonNull).reduce(0.0, Double::sum);
            GcsBatchHanle gcsBatchHanle = new GcsBatchHanle();
            maSelectCommonUtil.buildSystemFields(gcsBatchHanle);
            gcsBatchHanle.setStatus("COMPLETED");
            //生成处置单号
            gcsBatchHanle.setHandleNo(genHandleNo(waterRuleId, componentId, dateStr));
            gcsBatchHanle.setBatchNo(batch);
            gcsBatchHanle.setSeq(batchItem.getSeq());
            gcsBatchHanle.setHandleStatus("[\"PENDING_EVALUATION\"]");
            gcsBatchHanle.setHandleMod(MaSelectCommonUtil.insertBracket(handleWay));
            gcsBatchHanle.setHandleTag(MaSelectCommonUtil.insertBracket(handleTag));
            gcsBatchHanle.setMaterialModel(batchItem.getMaterialModel());
            gcsBatchHanle.setProductType(batchItem.getProductType());
            gcsBatchHanle.setBelongBase(batchItem.getProductionBaseDic());
            gcsBatchHanle.setHandleUniqTime(timestamp);
            gcsBatchHanle.setHandleStaticClass(handleType);
            gcsBatchHanle.setRemark(handleRemark);
            gcsBatchHanle.setInStorageDate(batchItem.getStorageDate());
            gcsBatchHanleList.add(gcsBatchHanle);
            //锁定批次数据
            batchItem.setCurrentWeightKg(Optional.ofNullable(batchItem.getCurrentWeightKg()).orElse(0.0) - batchLockWeight);
            batchItem.setLockWeightKg(Optional.ofNullable(batchItem.getLockWeightKg()).orElse(0.0) + batchLockWeight);
            updateBatchList.add(batchItem);
            for (TmatInfo tmatInfo : packItemList) {
                tmatInfo.setDisposalMethod(MaSelectCommonUtil.insertBracket(handleWay));
                //关联处置单
                tmatInfo.setPackHandle(gcsBatchHanle.getHandleNo());
                tmatInfo.setDisposalSuggestion(handleType);
                tmatInfo.setLockWeightKg(tmatInfo.getBalanceWeightKg());
                tmatInfo.setCurrentWeightKg(0.0);
                tmatInfo.setHandleTag(MaSelectCommonUtil.insertBracket(handleTag));
                tmatInfo.setPickStatus("[\"LOCK\"]");
                updatePackList.add(tmatInfo);
            }
        }

        if (CollectionUtils.isNotEmpty(gcsBatchHanleList)) {
            //批量插入处置单
            materialSelectDao.insertGcsBatchHanleList(gcsBatchHanleList);
            //更新批次表
            for (TbatchCommonInfo tbatchCommonInfo : updateBatchList) {
                materialSelectDao.updateBatchWeight(tbatchCommonInfo);
            }
            //更新包次表
            for (TmatInfo tmatInfo : updatePackList) {
                materialSelectDao.updatePackWeight(tmatInfo);
            }
        }

        return Response.ok();
    }

    @Override
    public List<Tbase> getPowerBaseList() {
        List<String> powerBaseList = new ArrayList<>();
        String userId = runtimeAppContextService.getCurrentUserId();
        String appId = runtimeAppContextService.getCurrentAppId();
        //查询权限下的基地
        List<ApaasRoleUserParams> apaasRoleUserParams = materialSelectDao.queryRolePowerBaseList(appId, userId);
        if (CollectionUtils.isNotEmpty(apaasRoleUserParams)) {
            List<String> values = apaasRoleUserParams.stream().map(m -> m.getParamValues()).collect(Collectors.toList());
            for (String value : values) {
                List<String> baseParams = JSON.parseArray(value, String.class);
                powerBaseList.addAll(baseParams);
            }
            powerBaseList = powerBaseList.stream().distinct().collect(Collectors.toList());
        }
        List<Tbase> tbases = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(powerBaseList)) {
            tbases = materialSelectDao.queryPowerBaseList(powerBaseList);
        }
        return tbases;
    }

    //挑料单权限控制 只能查看所属部门及下级部门发起的挑料单
    @Override
    public List<TmaterialSelectionCustomer> getPowerSelectOrder() {
        List<TmaterialSelectionCustomer> powerSelectOrderList = new ArrayList<>();
        String userId = runtimeAppContextService.getCurrentUserId();
        powerSelectOrderList = materialSelectDao.queryPowerSelectOrder(userId);
        List<Tbase> powerBaseList = this.getPowerBaseList();
        if(CollectionUtils.isNotEmpty(powerBaseList)){
            List<String> baseIds = powerBaseList.stream().map(m -> m.getId()).collect(Collectors.toList());
            powerSelectOrderList = powerSelectOrderList.stream().filter(m -> baseIds.contains(m.getProductionBase())).collect(Collectors.toList());
        }
        return powerSelectOrderList;
    }

    public List<Map<String, Object>> sortDataMapList(List<Map<String, Object>> dataMapList) {
        //入库日期 升序
        //相同的批次号在一起
        //排序号 升序
        if (CollectionUtils.isNotEmpty(dataMapList)) {
            dataMapList.sort(Comparator
                    .comparing((Map<String, Object> m) -> (Date) m.get("storageDate"), Comparator.nullsLast(Date::compareTo))
                    .thenComparing((Map<String, Object> m) -> (String) m.get("batch"), Comparator.nullsFirst(String::compareTo))
                    .thenComparing((Map<String, Object> m) -> (String) m.get("customerCode"), Comparator.nullsFirst(String::compareTo))
                    .thenComparing((Map<String, Object> m) -> (Integer) m.get("seq"), Comparator.nullsFirst(Integer::compareTo)));

        }
        return dataMapList;
    }


    public void handleReportData(List<ReportDataVo> reportDataVos, List<Map<String, Object>> dataMapList, Map<String,
            HeaderProjectDto> headerProjectMap, ReportQueryDto reportQueryDto) {
        // 添加终止条件：如果列表为空则直接返回
        if (reportDataVos == null || reportDataVos.isEmpty()) {
            return;
        } else {
            Map<String, List<ReportDataVo>> rowGroup = reportDataVos.stream().collect(Collectors.groupingBy(m -> m.getBatch() + "_" + m.getCustomerCode()));
            for (String rowKey : rowGroup.keySet()) {
                List<ReportDataVo> rowDatas = rowGroup.get(rowKey);
                ReportDataVo fixedCell = rowDatas.get(0);
                Map<String, Object> dataMap = new LinkedHashMap<>();
                dataMap.put("batch", fixedCell.getBatch());
                dataMap.put("materialModel", fixedCell.getMaterialModel());
                dataMap.put("balanceWeight", fixedCell.getBalanceWeight());
                dataMap.put("judgeResult", fixedCell.getTResult());
                dataMap.put("storageDate", fixedCell.getStorageDate());
                dataMap.put("customerCode", fixedCell.getCustomerCode());
                dataMap.put("documentId", fixedCell.getDocumentId());
                dataMap.put("currentWeightKg", fixedCell.getCurrentWeightKg());
                dataMap.put("tagName", fixedCell.getTagName());
                dataMap.put("pickStatus", fixedCell.getPickStatus());
                dataMap.put("seq", fixedCell.getSeq());
                //处理库龄
                long storageAge = getStorageAge(fixedCell);
                dataMap.put("storageAge", storageAge);
                dataMap.put("remark", fixedCell.getNotes());
                List<ReportDataVo> repeatDataVos = new ArrayList<>();
                for (ReportDataVo dynamicData : rowDatas) {
                    String projectName = dynamicData.getProjectName();
                    if (dataMap.get("Project-" + projectName) == null && headerProjectMap.get("Project-" + projectName) != null) {
                        List<ReportDataVo> collect = rowDatas.stream().filter(d -> projectName.equals(d.getProjectName())).collect(Collectors.toList());
                        if (collect.size() == 1) {
                            buildCellDynamicData(dynamicData, headerProjectMap, dataMap);
                        } else {
                            //多个重复的检查项，取修改时间最新的
                            repeatDataVos.addAll(collect);
                            Optional<ReportDataVo> max = repeatDataVos.stream().filter(vo -> projectName.equals(vo.getProjectName())).max(Comparator.comparing(ReportDataVo::getModifyTime));
                            // 如果需要获取对象（如果存在）
                            ReportDataVo latestDataVo = max.orElse(null);// 或者使用其他默认值
                            buildCellDynamicData(latestDataVo, headerProjectMap, dataMap);
                            repeatDataVos.remove(latestDataVo);
                        }
                    }
                }
                dataMapList.add(dataMap);
                //处理重复项目名称
                handleReportData(repeatDataVos, dataMapList, headerProjectMap, reportQueryDto);
            }
        }
    }

    public void buildCellDynamicData(ReportDataVo dynamicData, Map<String, HeaderProjectDto> headerProjectMap, Map<String, Object> dataMap) {
        String projectName = dynamicData.getProjectName();
        HeaderProjectDto headerProjectDto = headerProjectMap.get("Project-" + projectName);
        if (headerProjectDto == null) {
            return;
        }
        DynamicDataCell dynamicDataCell = new DynamicDataCell();
        dynamicDataCell.setValue(dynamicData.getValue());
        dynamicDataCell.setCellResult(dynamicData.getCellResult());
        Double tUsl = headerProjectDto.getTUsl();
        Double tLsl = headerProjectDto.getTLsl();
        Double lowerDeviation = Optional.ofNullable(dynamicData.getLowerDeviation()).orElse(0.0);
        Double upperDeviation = Optional.ofNullable(dynamicData.getUpperDeviation()).orElse(0.0);
        dynamicDataCell.setProjectId(dynamicData.getProjectId());
        //上限
        dynamicDataCell.setTUsl(tUsl);
        //下限
        dynamicDataCell.setTLsl(tLsl);
        //下偏差
        dynamicDataCell.setLowerDeviation(lowerDeviation);
        //上偏差
        dynamicDataCell.setUpperDeviation(upperDeviation);
        dynamicDataCell.setBackground("NoFill");
        //数值类指标超出或低于20%以上填充红色，超出或低于0%-20%以内填充黄色。
        double deviationPercent = 0.0;
        //上限和下限都有
        if (tUsl != null && tLsl != null) {
            if (lowerDeviation < tLsl) {
                deviationPercent = lowerDeviation / tLsl * 100;
            }
            if (upperDeviation > tUsl) {
                double upPercent = upperDeviation / tUsl * 100;
                if(upPercent > deviationPercent){
                    deviationPercent = upPercent;
                }
            }
        }
        //只有下限
        if (tUsl == null && tLsl != null) {
            deviationPercent = lowerDeviation / tLsl * 100;
        }
        //只有上限
        if (tUsl != null && tLsl == null) {
            deviationPercent = upperDeviation / tUsl * 100;
        }
        //四舍五入避免精度误差
        deviationPercent = BigDecimal.valueOf(deviationPercent).setScale(4, RoundingMode.HALF_UP).doubleValue();
        if (deviationPercent > 20) {
            dynamicDataCell.setBackground("Red");
        } else if (deviationPercent > 0) {
            dynamicDataCell.setBackground("Yellow");
        }
        dataMap.put("Project-" + projectName, dynamicDataCell);
    }

    //计算库龄
    public Long getStorageAge(ReportDataVo fixedCell) {
        if (fixedCell.getOutDate() == null) {
            fixedCell.setOutDate(new Date());
        }
        Date storageDate = fixedCell.getStorageDate();
        Date outDate = fixedCell.getOutDate();

        // 将日期的时间部分清零，只保留年月日信息
        Calendar storageCal = Calendar.getInstance();
        storageCal.setTime(storageDate);
        storageCal.set(Calendar.HOUR_OF_DAY, 0);
        storageCal.set(Calendar.MINUTE, 0);
        storageCal.set(Calendar.SECOND, 0);
        storageCal.set(Calendar.MILLISECOND, 0);

        Calendar outCal = Calendar.getInstance();
        outCal.setTime(outDate);
        outCal.set(Calendar.HOUR_OF_DAY, 0);
        outCal.set(Calendar.MINUTE, 0);
        outCal.set(Calendar.SECOND, 0);
        outCal.set(Calendar.MILLISECOND, 0);

        // 计算天数差（按年月日计算）
        long diffInMillies = Math.abs(outCal.getTimeInMillis() - storageCal.getTimeInMillis());
        long storageAge = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);
        return storageAge;
    }

    /**
     * 提取所有 children 中的对象
     */
    public static List<HeaderProjectDto> flattenChildren(List<HeaderDto> headers) {
        return headers.stream().filter(h -> h.getChildren() != null && !h.getChildren().isEmpty())
                .flatMap(h -> h.getChildren().stream()).collect(Collectors.toList());
    }


    public String genHandleNo(String waterRuleId, String componentId, String dateStr) {
        String handleNo = "gcs_h";
        handleNo = handleNo + dateStr;
        //查询当前流水码
        XdapWaterCodeStream waterValue = materialSelectDao.getWaterValue(waterRuleId, componentId);
        if (waterValue != null && StringUtils.isNotBlank(waterValue.getCodeValue())) {
            Integer codeValue = Integer.valueOf(waterValue.getCodeValue()) + 1;
            handleNo = handleNo + String.format("%04d", codeValue);
        } else {
            handleNo = handleNo + "0001";
        }
        //流水号+1
        waterValue.setCodeValue(Integer.valueOf(waterValue.getCodeValue()) + 1 + "");
        materialSelectDao.addCodeValue(waterValue);
        return handleNo;
    }

    /**
     * 判断字符串数值是否在 [min, max] 区间内（闭区间）
     *
     * @param dataValue 待比较的值（字符串）
     * @param minValue  区间下限，null 表示不限制
     * @param maxValue  区间上限，null 表示不限制
     * @return true：在范围内；false：不在或无法比较
     */
    public static boolean isInRange(String dataValue, Double minValue, Double maxValue) {
        // 1. 字符串为空直接判否
        if (dataValue == null) {
            return false;
        }

        // 2. 去掉首尾空格
        dataValue = dataValue.trim();

        // 3. 判断是否为合法数字（支持小数、科学计数法）
        if (!NumberUtils.isCreatable(dataValue)) {
            return false;
        }

        double val;
        try {
            val = Double.parseDouble(dataValue);
        } catch (NumberFormatException e) {
            return false;
        }

        // 4. 排除 NaN、无穷
        if (Double.isNaN(val) || Double.isInfinite(val)) {
            return false;
        }

        // 5. 区间判断
        if (minValue != null && val < minValue) {
            return false;
        }
        if (maxValue != null && val > maxValue) {
            return false;
        }

        return true;
    }

    /**
     * 添加权限控制
     * @param reportQueryDto
     * @return
     */
    public ReportQueryDto addBasePower(ReportQueryDto reportQueryDto){
        //查询：基地权限控制 只能查询所属基地的数据 即 mf_org 参数文本值
        List<String> queryBaseList = reportQueryDto.getBase();
        List<Tbase> powerBaseList = this.getPowerBaseList();
        List<String> powerBaseIdList = powerBaseList.stream().map(m -> m.getId()).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(queryBaseList)){
            reportQueryDto.setBase(powerBaseIdList);
        }
        return reportQueryDto;
    }
}
